tokenlock
This crate provides a cell type, TokenLock
, which can only be borrowed
by presenting the correct unforgeable token, thus decoupling permissions
from data.
Examples
Basics
// Create a token
let mut token = new;
// Create a keyhole by `token.id()` and use this to create a `TokenLock`.
let lock: = new;
assert_eq!;
// Unlock the `TokenLock` using the matching token
let mut guard = lock.write;
assert_eq!;
*guard = 2;
Only the matching Token
's owner can access its contents. Token
cannot be cloned:
let lock = new;
let lock_1 = clone;
spawn;
// can't access the contents; I no longer have `Token`
// lock.write(&mut token);
Zero-sized tokens
Some token types, such as BrandedToken
and SingletonToken
, rely
solely on type safety and compile-time checks to guarantee uniqueness and
don't use runtime data for identification. As such, the keyholes for such
tokens can be default-constructed. TokenLock::wrap
lets you construct a
TokenLock
with a default-constructed keyhole.
On the other hand, creating such tokens usually has specific requirements.
See the following example that uses with_branded_token
:
with_branded_token;
Lifetimes
The lifetime of the returned reference is limited by both of the TokenLock
and Token
.
let mut token = new;
let lock = new;
let guard = lock.write;
drop; // compile error: `guard` cannot outlive `TokenLock`
drop;
drop; // compile error: `guard` cannot outlive `Token`
drop;
It also prevents from forming a reference to the contained value when there already is a mutable reference to it:
let write_guard = lock.write;
let read_guard = lock.read; // compile error
drop;
While allowing multiple immutable references:
let read_guard1 = lock.read;
let read_guard2 = lock.read;
Use case: Linked lists
An operating system kernel often needs to store the global state in a global
variable. Linked lists are a common data structure used in a kernel, but
Rust's ownership does not allow forming 'static
references into values
protected by a mutex. Common work-arounds, such as smart pointers and index
references, take a heavy toll on a small microcontroller with a single-issue
in-order pipeline and no hardware multiplier.
static STATE: = todo!;
tokenlock
makes the 'static
reference approach possible by detaching the
lock granularity from the protected data's granularity.
use *;
use Cell;
;
impl_singleton_token_factory!;
type KLock<T> = ;
type KLockToken = ;
type KLockTokenId = ;
static STATE: SystemState = SystemState ;
Cell types
The TokenLock
type family is comprised of the following types:
Sync tokens |
!Sync tokens² |
|
---|---|---|
Unpinned | TokenLock |
UnsyncTokenLock |
Pinned¹ | PinTokenLock |
UnsyncPinTokenLock |
¹That is, these types respect T
being !Unpin
and prevent the
exposure of &mut T
through &Self
or Pin<&mut Self>
.
²Unsync*TokenLock
require that tokens are !Sync
(not sharable
across threads). In exchange, such cells can be Sync
even if the contained
data is not Sync
, just like std::sync::Mutex
.
Token types
This crate provides the following types implementing Token
.
(std
only) IcToken
uses a global counter (with thread-local pools)
to generate unique 128-bit tokens.
(alloc
only) RcToken
and ArcToken
ensure their uniqueness by
reference-counted memory allocations.
SingletonToken<Tag>
is a singleton token, meaning only one of such
instance can exist at any point of time during the program's execution.
impl_singleton_token_factory!
instantiates a static
flag to indicate
SingletonToken
's liveness and allows you to construct it safely by
SingletonToken::new
. Alternatively, you can use
SingletonToken::new_unchecked
, but this is unsafe if misused.
BrandedToken<'brand>
implements an extension of GhostCell
. It's
created by with_branded_token
or with_branded_token_async
, which
makes the created token available only within the provided closure or the
created Future
. This token incurs no runtime cost.
Token ID (keyhole) | Token (key) |
---|---|
IcTokenId |
IcToken + u128 comparison |
RcTokenId |
RcToken + usize comparison |
ArcTokenId |
ArcToken + usize comparison |
SingletonTokenId<Tag> |
SingletonToken<Tag> |
BrandedTokenId<'brand> |
BrandedToken<'brand> |
!Sync
tokens
UnsyncTokenLock
is similar to TokenLock
but designed for non-Sync
tokens and has relaxed requirements on the inner type for thread safety.
Specifically, it can be Sync
even if the inner type is not Sync
. This
allows for storing non-Sync
cells such as Cell
and reading and
writing them using shared references (all of which must be on the same
thread because the token is !Sync
) to the token.
use Cell;
let mut token = new;
let lock = new;
let lock_1 = clone;
spawn;
!Sync
tokens, of course, cannot be shared between threads:
let mut token = new;
let token = token.borrow_as_unsync;
let = ;
// compile error: `&ArcTokenUnsyncRef` is not `Send` because
// `ArcTokenUnsyncRef` is not `Sync`
spawn;
let _ = token_1;
Cargo Features
std
enables the items that depend onstd
oralloc
.alloc
enables the items that depend onalloc
.unstable
enables experimental items that are not subject to the semver guarantees.const-default_1
enables the implementation ofConstDefault
fromconst-default ^1
.
Related Work
-
ghost-cell
is the official implementation ofGhostCell
and has been formally proven to be sound. It provides an equivalent ofBrandedTokenLock
with a simpler, more focused interface. -
SCell
fromsingleton-cell
is a more generalized version ofGhostCell
and accepts any singleton token types, and thus it's more closer to ourTokenLock
. It provides equivalents of ourBrandedToken
andSingletonToken
out-of-box. It trades away non-ZST token types for an advantage:SCell<Key, [T]>
can be transposed to[SCell<Key, T>]
. It uses thesingleton-trait
crate (which did not exist whentokenlock::SingletonToken
was added) to mark singleton token types. -
qcell
provides multiple cell types with different check mechanisms.QCell
uses a 32-bit integer as a token identifier,TCell
andTLCell
use a marker type, andLCell
uses lifetime branding. -
TokenCell
fromtoken-cell
is related to ourSingletonToken
, but likeSCell
(but differing slightly), it supports transposition from&TokenCell<Token, &[T]>
to&[TokenCell<Token, T>]
. It uses a custom trait to mark singleton token types.
License: MIT/Apache-2.0